import 'dart:collection';
import 'dart:convert';
import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:video_player/video_player.dart' as vp;
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import 'package:image/image.dart' as it;

import '../common/api_service.dart';
import '../common/events.dart';
import '../common/logger.dart';
import '../common/routes.dart';
import '../common/sharedPreferences_helper.dart';
import '../common/utils.dart';
import '../model/common_mode.dart';
import '../model/pipe_album_model.dart';
import '../model/pipe_scene_model.dart';
import '../model/chat_message.dart';
import '../model/role_model.dart';
import '../service/auth_service.dart';
import '../widgets/dialog.dart';

class ChatController extends FullLifeCycleController with FullLifeCycleMixin, RouteAware {
  final String _pipeId = Get.arguments['pipeId'] ?? '';
  final TextEditingController textController = TextEditingController();
  final FocusNode focusNode = FocusNode();

  var appState = AppLifecycleState.resumed.obs;
  PipeSceneModel? pipeSceneModel;
  var showSendButton = false.obs;
  final RxList<ChatMessage2> messages = <ChatMessage2>[].obs;
  var bgType = "".obs;
  var bgSrcUrl = "".obs;
  var shortbtns = <MessageShortcutModel>[].obs;
  var quickInputSheetButtonList = <QuickReply2>[].obs;
  var disabledInput = false.obs;
  var isTyping = false.obs;
  var isPanelVisible = false.obs;
  var videoInitialized = false.obs;
  var isMute = false.obs;
  var inputType = "".obs;
  var isCustomReply = false.obs;
  var selectedImagePath = "".obs;
  var selectedImageUrl = "".obs;
  var isUploadImage = false.obs;
  var commentCount = 0.obs;

  vp.VideoPlayerController? videoPlayerController;
  AudioPlayer? _audioPlayer;
  final Map<String, ChatUser2> _memberMap = <String, ChatUser2>{};
  final ChatUser2 _currentUser = ChatUser2(id: Get.find<AuthService>().userModel?.uid ?? "", nickname: '', avatarUrl: '');
  Timer? _timer;
  final Queue<String> _audioQueue = Queue<String>();

  final RouteObserver<PageRoute> routeObserver = Get.find<RouteObserver<PageRoute>>();

  void subscribeToRoute(PageRoute route) {
    routeObserver.unsubscribe(this);
    routeObserver.subscribe(this, route);
  }

  @override
  void onInit() {
    super.onInit();

    focusNode.addListener(() {
      if (focusNode.hasFocus) {
        isPanelVisible.value = false;
      }
    });

    textController.addListener(() {
      if (inputType.value == "str") {
        showSendButton.value = textController.text.trim().isNotEmpty;
      } else {
        if (textController.text.trim().isNotEmpty && selectedImageUrl.value.isNotEmpty) {
          showSendButton.value = true;
        } else {
          showSendButton.value = false;
        }
      }
    });

    _getPipeDetail();
    _getPipeCommentsCount();
  }

  @override
  void onDetached() {}

  @override
  void onHidden() {}

  @override
  void onInactive() {}

  @override
  void onPaused() {
    videoPlayerController?.pause();
    _audioPlayer?.pause();
  }

  @override
  void onResumed() {
    if ((videoPlayerController?.value.isInitialized ?? false) == true) {
      videoPlayerController?.play();
    }
    _audioPlayer?.resume();
  }

  @override
  void onClose() {
    routeObserver.unsubscribe(this);
    focusNode.dispose();
    textController.dispose();
    videoPlayerController?.dispose();
    _audioPlayer?.dispose();
    _stopTimer();
    super.onClose();
  }

  @override
  void didPopNext() async {
    super.didPopNext();
    await _audioPlayer?.resume();
    if ((videoPlayerController?.value.isInitialized ?? false) == true) {
      await videoPlayerController?.play();
    }
  }

  @override
  void didPushNext() async {
    super.didPushNext();
    await _audioPlayer?.pause();
    await videoPlayerController?.pause();
  }

  _getPipeCommentsCount() {
    ApiService().getPipeCommentsCount(_pipeId, (data) {
      if (data is int) {
        commentCount.value = data;
      }
    }, () {});
  }

  autoPlayAudio() {
    if (_audioQueue.isNotEmpty) {
      final audioUrl = _audioQueue.removeFirst();
      eventBus.fire(PlayAudioEvent(audioUrl: audioUrl, autoPlay: true));
    }
  }

  clearAutoPlayAudioQueue() {
    _audioQueue.clear();
  }

  _sendShortcutMessage(String content, {bool sendHidden = false}) {
    quickInputSheetButtonList.clear();
    final ChatMessage2 message = ChatMessage2(
      user: _currentUser,
      timestamp: DateTime.now(),
    );
    message.isMine = true;
    message.text = content;
    if (!sendHidden) {
      messages.insert(0, message);
    }
    requestSendTextMessage(message.text);
  }

  sendShortcutMessage(MessageShortcutModel message) {
    var subMsg = message.sub;
    var fn = subMsg.where((x) => x.inName == "fn").first.val ?? "";
    if (fn == "inParam" || fn == "sendHidden") {
      bool sendHidden = fn == "sendHidden";
      _sendShortcutMessage(message.val, sendHidden: sendHidden);
    } else if (fn == "jumpPipe") {
      var pipeId = subMsg.where((x) => x.inName == "args0").first.val ?? "";
      Get.toNamed(Routes.CHAT, arguments: {"pipeId": pipeId}, preventDuplicates: false);
    } else if (fn == "clearChat") {
      clearChat();
    } else {
      debugPrint("fn=$fn");
    }
  }

  sendShortcut(String content, MessageShortcutModel message) {
    var subMsg = message.sub;
    var fn = subMsg.where((x) => x.inName == "fn").first.val ?? "";
    if (fn == "inParam" || fn == "sendHidden") {
      bool sendHidden = fn == "sendHidden";
      _sendShortcutMessage(content, sendHidden: sendHidden);
    } else if (fn == "jumpPipe") {
      var pipeId = subMsg.where((x) => x.inName == "args0").first.val ?? "";
      Get.toNamed(Routes.CHAT, arguments: {"pipeId": pipeId});
    } else if (fn == "clearChat") {
      clearChat();
    } else {
      debugPrint("fn=$fn");
    }
  }

  sendPipeAlbum(String content, PipeAlbumMessageModel message) {
    var subMsg = message.sub ?? [];
    var fn = subMsg.where((x) => x.name == "fn").first.val;
    if (fn == "inParam" || fn == "sendHidden") {
      bool sendHidden = fn == "sendHidden";
      _sendShortcutMessage(content, sendHidden: sendHidden);
    } else if (fn == "jumpPipe") {
      var pipeId = subMsg.where((x) => x.name == "args0").first.val;
      Get.toNamed(Routes.CHAT, arguments: {"pipeId": pipeId});
    } else if (fn == "clearChat") {
      clearChat();
    } else {
      debugPrint("fn=$fn");
    }
  }

  void sendMessageItem(String content, MessageItemModel message) {
    var subMsg = message.sub ?? [];
    var fn = subMsg.where((x) => x.inName == "fn").first.val ?? "";
    if (fn == "inParam" || fn == "sendHidden") {
      bool sendHidden = fn == "sendHidden";
      _sendShortcutMessage(content, sendHidden: sendHidden);
    } else if (fn == "jumpPipe") {
      var pipeId = subMsg.where((x) => x.inName == "args0").first.val ?? "";
      Get.toNamed(Routes.CHAT, arguments: {"pipeId": pipeId});
    } else {
      debugPrint("fn=$fn");
    }
  }

  void sendQuickReply(QuickReply2 r) {
    if (r.customProperties != null && r.customProperties!["data"] != null) {
      final data = r.customProperties!["data"];
      if (data is MessageShortcutModel) {
        MessageShortcutModel message = data;
        sendShortcut(r.title, message);
      } else if (data is PipeAlbumMessageModel) {
        PipeAlbumMessageModel message = data;
        sendPipeAlbum(r.title, message);
      } else if (data is MessageItemModel) {
        MessageItemModel message = data;
        sendMessageItem(r.title, message);
      } else {
        _sendShortcutMessage(r.title);
      }
    } else {
      _sendShortcutMessage(r.title);
    }
  }

  sendAction() {
    if (inputType.value == "str") {
      sendTextMessage();
    } else if (inputType.value == "img") {
      sendImageMessage();
    } else if (inputType.value == "mixed") {
      sendTextImageMessage();
    }
  }

  void sendTextImageMessage() {
    List<ChatMedia2> medias = [];
    var media = ChatMedia2(
      selectedImageUrl.value,
      MediaType2.image,
    );
    medias.add(media);

    var message = ChatMessage2(user: _currentUser, timestamp: DateTime.now());
    message.text = textController.text;
    message.isMine = true;
    message.medias = medias;

    messages.insert(0, message);

    requestSendTextImageMessage(textController.text, selectedImageUrl.value);
    textController.clear();
  }

  void sendImageMessage() {
    List<ChatMedia2> medias = [];
    var media = ChatMedia2(
      selectedImageUrl.value,
      MediaType2.image,
    );
    medias.add(media);

    var message = ChatMessage2(user: _currentUser, timestamp: DateTime.now());
    message.isMine = true;
    message.medias = medias;

    messages.insert(0, message);

    _requestSendImageMessage();
  }

  sendAudioMessage(String voiceUrl, String voiceText) {
    List<ChatMedia2> medias = [];
    if (voiceUrl.isNotEmpty) {
      var media = ChatMedia2(
        voiceUrl,
        MediaType2.audio,
      );
      medias.add(media);
    }
    var message = ChatMessage2(user: _currentUser, timestamp: DateTime.now());
    message.isMine = true;
    message.medias = medias;
    if (voiceText.isNotEmpty) {
      message.text = voiceText;
    }
    messages.insert(0, message);

    _requestSendAudioMessage(voiceUrl, voiceText);
  }

  sendTextMessage() {
    if (textController.text.isNotEmpty) {
      final ChatMessage2 message = ChatMessage2(
        user: _currentUser,
        timestamp: DateTime.now(),
      );
      message.isMine = true;
      message.text = textController.text;
      messages.insert(0, message);
      requestSendTextMessage(message.text);
    }
  }

  _requestSendAudioMessage(String voiceUrl, String voiceText) {
    isTyping.value = true;

    var params = <String, dynamic>{};
    params["pipeId"] = _pipeId;

    if (voiceUrl.isNotEmpty && voiceText.isNotEmpty) {
      List<Map<String, dynamic>> data = [];
      var m1 = <String, dynamic>{};
      m1["type"] = "audio";
      m1["name"] = "in_speak";
      m1["val"] = voiceUrl;
      data.add(m1);

      var m2 = <String, dynamic>{};
      m2["type"] = "str";
      m2["name"] = pipeSceneModel?.pipe.inParams?.first.name ?? "k_1";
      m2["val"] = voiceText;
      data.add(m2);

      params["in"] = data;
    } else {
      if (voiceUrl.isEmpty) {
        var inMap = <String, dynamic>{};
        inMap["type"] = "audio";
        inMap["name"] = "in_speak";
        inMap["val"] = voiceUrl;

        params["in"] = [inMap];
        params["voiceName"] = pipeSceneModel?.pipe.inParams?.first.name ?? "k_1";
      } else {
        if (voiceText.isEmpty) {
          var inMap = <String, dynamic>{};
          inMap["type"] = "str";
          inMap["name"] = pipeSceneModel?.pipe.inParams?.first.name ?? "k_1";
          inMap["val"] = voiceText;

          params["in"] = [inMap];
        }
      }
    }

    ApiService().sendMessage(params, (data) {
      selectedImageUrl.value = "";
      selectedImagePath.value = "";
      showSendButton.value = false;

      String albumId = data as String;
      _startTimer(callback: _onTimer, args: albumId);
    }, () {});
  }

  void _requestSendImageMessage() {
    isTyping.value = true;

    var params = <String, dynamic>{};
    params["pipeId"] = _pipeId;

    var inMap = <String, dynamic>{};
    inMap["type"] = "img";
    inMap["name"] = pipeSceneModel?.pipe.inParams?.first.name ?? "k_1";
    inMap["val"] = selectedImageUrl.value;

    params["in"] = [inMap];

    ApiService().sendMessage(params, (data) {
      selectedImageUrl.value = "";
      selectedImagePath.value = "";
      showSendButton.value = false;

      String albumId = data as String;
      _startTimer(callback: _onTimer, args: albumId);
    }, () {});
  }

  void requestSendTextImageMessage(String text, String imageUrl) {
    isTyping.value = true;

    var params = <String, dynamic>{};
    params["pipeId"] = _pipeId;

    List<Map<String, dynamic>> data = [];
    var m1 = <String, dynamic>{};
    m1["type"] = "img";
    m1["name"] = pipeSceneModel?.pipe.inParams?.firstWhereOrNull((x) => x.type == "img")?.name ?? "k_2";
    m1["val"] = imageUrl;
    data.add(m1);

    var m2 = <String, dynamic>{};
    m2["type"] = "str";
    m2["name"] = pipeSceneModel?.pipe.inParams?.firstWhereOrNull((x) => x.type == "str")?.name ?? "k_1";
    m2["val"] = text;
    data.add(m2);

    params["in"] = data;

    ApiService().sendMessage(params, (data) {
      String albumId = data as String;
      _startTimer(callback: _onTimer, args: albumId);
      if (quickInputSheetButtonList.isNotEmpty) {
        quickInputSheetButtonList.clear();
      }
      selectedImageUrl.value = "";
      selectedImagePath.value = "";
      showSendButton.value = false;
      textController.clear();
    }, () {});
  }

  void requestSendTextMessage(String text) {
    isTyping.value = true;

    var params = <String, dynamic>{};
    params["pipeId"] = _pipeId;

    var inMap = <String, dynamic>{};
    inMap["type"] = "str";
    inMap["name"] = pipeSceneModel?.pipe.inParams?.first.name ?? "k_1";
    inMap["val"] = text;

    params["in"] = [inMap];

    ApiService().sendMessage(params, (data) {
      String albumId = data as String;
      _startTimer(callback: _onTimer, args: albumId);
      if (quickInputSheetButtonList.isNotEmpty) {
        quickInputSheetButtonList.clear();
      }
    }, () {});
  }

  void _startTimer({int sec = 3, Function? callback, dynamic args}) {
    _stopTimer();
    _timer = Timer.periodic(Duration(seconds: sec), (timer) {
      if (callback != null) {
        callback(args);
      }
    });
  }

  void _stopTimer() {
    if (_timer != null) {
      _timer?.cancel();
      _timer = null;
    }
  }

  void _onTimer(dynamic args) {
    String albumId = args as String;
    _getAlbumMessage(albumId);
  }

  void _getAlbumMessage(String albumId) {
    ApiService().getPipeAlbumProgress(albumId, (PipeAlbumModel data) {
      if (data.progress == "fail") {
        isTyping.value = false;
        _stopTimer();
      } else if (data.progress == "completed") {
        isTyping.value = false;
        _stopTimer();
        if (data.content != null && data.content!.isNotEmpty) {
          _getReplyMessage(data.content!);
        }
      }
    }, () {
      _stopTimer();
    });
  }

  void _getReplyMessage(String content) {
    try {
      List<ChatMessage2> allMessages = [];
      List<QuickReply2> quickReplies = [];
      var data = json.decode(content) as List<dynamic>;
      var pipeAlbumMessageList = data.map((e) => PipeAlbumMessageModel.fromJson(e as Map<String, dynamic>)).toList();
      var groupedMessages = LinkedHashMap<String, List<PipeAlbumMessageModel>>.of({});
      for (final message in pipeAlbumMessageList) {
        if (!groupedMessages.containsKey(message.group)) {
          groupedMessages[message.group!] = [];
        }
        groupedMessages[message.group!]!.add(message);
      }
      for (var group in groupedMessages.keys) {
        var pipeAlbumMessageList = groupedMessages[group] ?? [];
        var chatUser = _getChatUser(group, isGreet: false);
        var chatMessage = ChatMessage2(user: chatUser, timestamp: DateTime.now());
        String msgType = _getMessageType(group, false);
        if (msgType == "system" || msgType == "narration") {
          chatMessage.customProperties = {"data": pipeAlbumMessageList};
          if (msgType == "system") {
            chatMessage.messageType = MessageType.system;
          } else {
            chatMessage.messageType = MessageType.narration;
          }
          allMessages.add(chatMessage);
        } else {
          List<PipeAlbumMessageModel> buttonList = [];
          List<ChatMedia2> medias = [];
          for (final pipeAlbumMessage in pipeAlbumMessageList) {
            var name = pipeAlbumMessage.name ?? "";
            var type = pipeAlbumMessage.type ?? "";

            if (isBg(name) && pipeAlbumMessage.val.isNotEmpty) {
              _setBgSrc(pipeAlbumMessage);
            }

            var sheet = _isSheetMessage(group, isGreeting: false);
            if (isBtn(name) && !sheet && pipeAlbumMessage.val.isNotEmpty) {
              buttonList.add(pipeAlbumMessage);
              continue;
            }

            if (isSound(name, type) && pipeAlbumMessage.val.isNotEmpty) {
              _updateBgm(pipeAlbumMessage.val);
            }

            //IMAGE
            if (isImg(name, type) && pipeAlbumMessage.val.isNotEmpty) {
              var imageUrl = pipeAlbumMessage.val;
              if (_isBracketEnclosed(imageUrl)) {
                imageUrl = _extractContentBetweenBrackets(imageUrl);
              }
              var media = ChatMedia2(
                imageUrl,
                MediaType2.image,
              );
              media.customProperties = {"data": pipeAlbumMessage};
              medias.add(media);
            }

            //AUDIO
            if (isAudio(name, type) && pipeAlbumMessage.val.isNotEmpty) {
              var media = ChatMedia2(pipeAlbumMessage.val, MediaType2.audio);
              medias.add(media);
              _audioQueue.add(pipeAlbumMessage.val);
            }

            if (isText(name, type) && pipeAlbumMessage.val.isNotEmpty) {
              bool m = isMarkdown(pipeAlbumMessage.val);
              if (m == true) {
                chatMessage.isMarkdown = true;
              }
              var text = chatMessage.text;
              if (isEmpty(text)) {
                chatMessage.text = pipeAlbumMessage.val;
              } else {
                text = '$text<br>${pipeAlbumMessage.val}';
                chatMessage.text = text;
              }
            }
          }
          if (buttonList.isNotEmpty) {
            chatMessage.buttonList = buttonList;
          }
          if (medias.isNotEmpty) {
            chatMessage.medias = medias;
          }
          if (chatMessage.text.isNotEmpty || chatMessage.buttonList.isNotEmpty || chatMessage.medias.isNotEmpty) {
            allMessages.add(chatMessage);
          }
        }
      }

      //check the last message is or not sheet
      if (groupedMessages.entries.isNotEmpty) {
        var m = groupedMessages.entries.last;
        _getQuickInputSheetList2(m.key, m.value, false);
      }

      allMessages = allMessages.reversed.toList();
      messages.insertAll(0, allMessages);

      quickReplies.clear();
      quickReplies.addAll(quickReplies);

      Future.delayed(Duration(milliseconds: 500), () {
        autoPlayAudio();
      });
    } catch (e) {
      logger.e(e);
    }
  }

  Future<void> onToggleMute() async {
    isMute.value = !isMute.value;
    if (isMute.value) {
      await _audioPlayer?.setVolume(0);
      await videoPlayerController?.setVolume(0);
    } else {
      var volume = pipeSceneModel?.pipe.wrapper?.bgm?.volume ?? 1.0;
      await _audioPlayer?.setVolume(volume);
      await videoPlayerController?.setVolume(1);
    }
    await SharedPreferencesHelper.setBool("chat_bgm_mute", isMute.value);
  }

  void _getPipeDetail() {
    ApiService().getPipeDetail(_pipeId, (model) {
      pipeSceneModel = model;
      _playBgm();
      _getBgSrc();
      _getChatMemberUsers();
      _getGreetingMessage();
      _getShortcutMessage();
      _getMessageList();

      final inParamsCount = pipeSceneModel?.pipe.inParams?.length ?? 0;
      if (inParamsCount == 0) {
        inputType.value = "str";
      } else if (inParamsCount == 1) {
        inputType.value = pipeSceneModel?.pipe.inParams?.first.type ?? "str";
      } else {
        inputType.value = "mixed";
      }
    }, (err) {
      if (err != null && err.toString().isNotEmpty) {
        Fluttertoast.showToast(msg: err.toString(), gravity: ToastGravity.TOP);
      }
      Get.back();
    });
  }

  Future<void> _updateBgm(String audioUrl) async {
    try {
      await _audioPlayer?.setReleaseMode(ReleaseMode.release);
      await _audioPlayer?.setSource(UrlSource(audioUrl));
      await _audioPlayer?.resume();
    } catch (e) {
      logger.e('Error playing audio: $e');
    }
  }

  Future<void> _playBgm() async {
    try {
      String? audioUrl;
      double volume = 1.0;
      int loop = 1;
      if (pipeSceneModel?.pipe.wrapper?.bgm != null) {
        audioUrl = pipeSceneModel?.pipe.wrapper?.bgm?.src ?? "";
      }
      volume = pipeSceneModel?.pipe.wrapper?.bgm?.volume ?? 1.0;
      loop = pipeSceneModel?.pipe.wrapper?.bgm?.loop ?? 0;

      if (audioUrl != null && audioUrl.isNotEmpty) {
        _audioPlayer ??= AudioPlayer();
        final mute = SharedPreferencesHelper.getBool("chat_bgm_mute") ?? false;
        if (mute) {
          await _audioPlayer?.setVolume(0);
          isMute.value = true;
        } else {
          await _audioPlayer?.setVolume(volume);
        }
        await _audioPlayer?.setReleaseMode(loop == 1 ? ReleaseMode.loop : ReleaseMode.release);
        await _audioPlayer?.setSource(UrlSource(audioUrl));
        await _audioPlayer?.resume();
      }
    } catch (e) {
      logger.e('Error playing audio: $e');
    }
  }

  void _setBgSrc(PipeAlbumMessageModel message) {
    bgSrcUrl.value = message.val;
    bgType.value = message.type ?? "";
    _refreshBgSrc();
  }

  void _getBgSrc() {
    bgSrcUrl.value = pipeSceneModel?.pipe.wrapper?.bg?.chat?.src ?? "";
    bgType.value = pipeSceneModel?.pipe.wrapper?.bg?.chat?.type ?? "";
    _refreshBgSrc();
  }

  void _refreshBgSrc() {
    String fileName = bgSrcUrl.value.split('/').last;
    String fileExtension = fileName.split('.').last;

    if (bgType.value.isEmpty) {
      if (isImageFile(fileExtension)) {
        bgType.value = "img";
      } else if (fileExtension == "mp4") {
        bgType.value = "video";
      }
    }

    if (bgType.value == "video") {
      videoPlayerController = vp.VideoPlayerController.networkUrl(Uri.parse(bgSrcUrl.value))
        ..initialize().then((_) {
          videoInitialized.value = true;
          videoPlayerController?.setLooping(true);
          videoPlayerController?.play();
        }).catchError((error) {
          debugPrint('Error during video initialization: $error');
        });
    }
  }

  void _getChatMemberUsers() {
    var users = pipeSceneModel?.pipe.attrList ?? [];
    for (var user in users) {
      var roleModel = RoleModel.fromJson(json.decode(user.val));
      var chatUser = ChatUser2(id: user.id, avatarUrl: roleModel.avatar ?? '', nickname: roleModel.nickname);
      if (!_memberMap.keys.contains(user.id)) {
        _memberMap[user.id] = chatUser;
      }
    }
    var roleMap = pipeSceneModel?.pipe.wrapper?.roles ?? <String, dynamic>{};
    for (var roleId in roleMap.keys) {
      var roleModel = RoleModel.fromJson(roleMap[roleId]);
      var chatUser = ChatUser2(id: roleId, avatarUrl: roleModel.avatar ?? '', nickname: roleModel.nickname);
      if (!_memberMap.keys.contains(roleId)) {
        _memberMap[roleId] = chatUser;
      }
    }
  }

  _getMessageType(String group, bool isGreeting) {
    String msgType = "";
    var groupMap = pipeSceneModel?.pipe.wrapper?.groupOut ?? <String, dynamic>{};
    if (isGreeting) {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupGreeting ?? <String, dynamic>{};
    }
    if (groupMap.keys.contains(group)) {
      final typeMap = groupMap[group] as Map<String, dynamic>;
      if (typeMap.keys.contains("type")) {
        final type = typeMap["type"];
        if (typeMap["ui"] != null) {
          final ui = typeMap["ui"] as Map<String, dynamic>;
          var name = ui["btnName"];
          if (name == 'system' || type == 'system') {
            msgType = type;
          }
          if (name == 'toast' || type == 'toast') {
            msgType = type;
          }
          if (name == 'narration' || type == 'narration') {
            msgType = type;
          }
        } else {
          msgType = type;
        }
      }
    }
    return msgType;
  }

  void _getGreetingMessage() {
    var groupedMessages = LinkedHashMap<String, List<MessageItemModel>>.of({});
    var greets = pipeSceneModel?.pipe.wrapper?.greetings ?? [];
    for (final message in greets) {
      if (!groupedMessages.containsKey(message.group)) {
        groupedMessages[message.group] = [];
      }
      groupedMessages[message.group]!.add(message);
    }
    //print(groupedMessages);

    List<ChatMessage2> allMessages = [];

    for (var group in groupedMessages.keys) {
      var messageList = groupedMessages[group] ?? [];
      var chatUser = _getChatUser(group, isGreet: true);
      String msgType = _getMessageType(group, true);
      //SYSTEM MESSAGE
      if (msgType == "system" || msgType == "narration") {
        var chatMessage = ChatMessage2(user: chatUser, timestamp: DateTime.now());
        chatMessage.customProperties = {"data": messageList};
        if (msgType == "system") {
          chatMessage.messageType = MessageType.system;
        } else {
          chatMessage.messageType = MessageType.narration;
        }
        allMessages.add(chatMessage);
      } else {
        var chatMessage = ChatMessage2(user: chatUser, timestamp: DateTime.now());
        List<MessageItemModel> buttonList = [];
        List<ChatMedia2> medias = [];
        for (var message in messageList) {
          var name = message.inName ?? "";
          var type = message.type ?? "";
          var sheet = _isSheetMessage(group, isGreeting: true);
          if (isBtn(name) && !sheet && message.val != null && message.val!.isNotEmpty) {
            buttonList.add(message);
            continue;
          }

          //AUDIO
          if (isAudio(name, type) && message.val != null && message.val!.isNotEmpty) {
            var media = ChatMedia2(message.val ?? "", MediaType2.audio);
            medias.add(media);
          }

          //TEXT
          if (isText(name, type) && message.val != null && message.val!.isNotEmpty) {
            var content = message.val ?? "";
            chatMessage.isMarkdown = isMarkdown(content);
            chatMessage.text = content;
          }

          //IMAGE
          if (isImg(name, type) && message.val != null && message.val!.isNotEmpty) {
            var media = ChatMedia2(message.val ?? "", MediaType2.image);
            media.customProperties = {"data": message};
            medias.add(media);
          }
        }
        if (buttonList.isNotEmpty) {
          chatMessage.buttonList = buttonList;
        }
        if (medias.isNotEmpty) {
          chatMessage.medias = medias;
        }
        if (chatMessage.text.isNotEmpty || chatMessage.buttonList.isNotEmpty || chatMessage.medias.isNotEmpty) {
          allMessages.add(chatMessage);
        }
      }
    }

    //check the last message is or not sheet
    if (groupedMessages.entries.isNotEmpty) {
      var messageList = groupedMessages.entries.last.value;
      var group = groupedMessages.entries.last.key;
      _getQuickInputSheetList(group, messageList, true);
    }
    allMessages = List<ChatMessage2>.from(allMessages).reversed.toList();
    messages.addAll(allMessages);
  }

  _getQuickInputSheetList(String group, List<MessageItemModel> messageList, bool greeting) {
    var sheet = _isSheetMessage(group, isGreeting: greeting);
    if (sheet) {
      _getSheetMessageList(group, messageList, greeting);
      if (quickInputSheetButtonList.isNotEmpty) {
        bool noInput = _getSheetNoInputState(group, isGreeting: greeting);
        if (!noInput) {
          var quickReply = QuickReply2(title: "自定义回复");
          quickReply.customProperties = {"noInput": noInput};
          quickInputSheetButtonList.add(quickReply);
        }
        disabledInput.value = true;
      }
    }
  }

  _getQuickInputSheetList2(String group, List<PipeAlbumMessageModel> messageList, bool greeting) {
    var sheet = _isSheetMessage(group, isGreeting: greeting);
    if (sheet) {
      _getSheetMessageList2(group, messageList, greeting);
      if (quickInputSheetButtonList.isNotEmpty) {
        bool noInput = _getSheetNoInputState(group, isGreeting: greeting);
        if (!noInput) {
          var quickReply = QuickReply2(title: "自定义回复");
          quickReply.customProperties = {"noInput": noInput};
          quickInputSheetButtonList.add(quickReply);
        }
        disabledInput.value = true;
        return;
      }
    }
    disabledInput.value = false;
  }

  void _getSheetMessageList(String group, List<MessageItemModel> messageList, bool greeting) {
    for (var message in messageList) {
      var name = message.inName ?? "";
      if (isBtn(name) && message.val != null && message.val!.isNotEmpty) {
        var content = message.val ?? "";
        if (content.isNotEmpty) {
          //SHEET MESSAGE BUTTON
          if (_isSheetMessage(group, isGreeting: greeting)) {
            var quickReply = QuickReply2(title: content);
            quickReply.customProperties = {"data": message};
            quickInputSheetButtonList.add(quickReply);
          }
        }
      }
    }
  }

  void _getSheetMessageList2(String group, List<PipeAlbumMessageModel> messageList, bool greeting) {
    for (var message in messageList) {
      var name = message.name ?? "";
      if (isBtn(name) && message.val.isNotEmpty) {
        var content = message.val;
        if (content.isNotEmpty) {
          //SHEET MESSAGE BUTTON
          if (_isSheetMessage(group, isGreeting: greeting)) {
            var quickReply = QuickReply2(title: content);
            quickReply.customProperties = {"data": message};
            quickInputSheetButtonList.add(quickReply);
          }
        }
      }
    }
  }

  void _getShortcutMessage() {
    shortbtns.value = pipeSceneModel?.pipe.wrapper?.shortbtns ?? [];
  }

  void _getMessageList() {
    try {
      var messageModelList = pipeSceneModel?.list.reversed ?? [];
      List<ChatMessage2> allMessages = [];

      for (final message in messageModelList) {
        var dateTime = DateTime.parse(message.createdTime);

        var input = message.inputs.where((x) => x.type == "str").firstOrNull;
        if (input != null) {
          var content = input.val;
          if (content.isNotEmpty) {
            var chatMessage = ChatMessage2(user: _currentUser, timestamp: dateTime);
            chatMessage.text = input.val;
            chatMessage.isMine = true;
            allMessages.add(chatMessage);
          }
        }
        input = message.inputs.where((x) => x.type == "img").firstOrNull;
        if (input != null) {
          var chatMessage = ChatMessage2(user: _currentUser, timestamp: dateTime);
          chatMessage.isMine = true;
          var imageUrl = input.val;
          if (_isBracketEnclosed(imageUrl)) {
            imageUrl = _extractContentBetweenBrackets(imageUrl);
          }
          List<ChatMedia2> medias = [];
          var media = ChatMedia2(
            imageUrl,
            MediaType2.image,
          );
          media.customProperties = {"data": message};
          medias.add(media);
          chatMessage.medias = medias;
          allMessages.add(chatMessage);
        }

        var pipeAlbumMessageList = message.content ?? [];

        var groupedMessages = LinkedHashMap<String, List<PipeAlbumMessageModel>>.of({});
        for (final message in pipeAlbumMessageList) {
          if (!groupedMessages.containsKey(message.group)) {
            groupedMessages[message.group!] = [];
          }
          groupedMessages[message.group!]!.add(message);
        }
        for (var group in groupedMessages.keys) {
          var pipeAlbumMessageList = groupedMessages[group] ?? [];
          var chatUser = _getChatUser(group, isGreet: false);
          var chatMessage = ChatMessage2(user: chatUser, timestamp: dateTime);

          String msgType = _getMessageType(group, false);
          if (msgType == "system" || msgType == "narration") {
            chatMessage.customProperties = {"data": pipeAlbumMessageList};
            if (msgType == "system") {
              chatMessage.messageType = MessageType.system;
            } else {
              chatMessage.messageType = MessageType.narration;
            }
            allMessages.add(chatMessage);
          } else {
            List<PipeAlbumMessageModel> buttonList = [];
            List<ChatMedia2> medias = [];
            for (final pipeAlbumMessage in pipeAlbumMessageList) {
              var name = pipeAlbumMessage.name ?? "";
              var type = pipeAlbumMessage.type ?? "";

              if (isBg(name) && pipeAlbumMessage.val.isNotEmpty) {
                _setBgSrc(pipeAlbumMessage);
              }

              //BUTTON LIST
              var sheet = _isSheetMessage(group, isGreeting: false);
              if (isBtn(name) && !sheet && pipeAlbumMessage.val.isNotEmpty) {
                buttonList.add(pipeAlbumMessage);
                continue;
              }

              //TEXT
              if (isText(name, type) && pipeAlbumMessage.val.isNotEmpty) {
                bool m = isMarkdown(pipeAlbumMessage.val);
                if (m == true) {
                  chatMessage.isMarkdown = true;
                }
                var text = chatMessage.text;
                if (isEmpty(text)) {
                  chatMessage.text = pipeAlbumMessage.val;
                } else {
                  text = '$text<br>${pipeAlbumMessage.val}';
                  chatMessage.text = text;
                }
              }

              //AUDIO
              if (isAudio(name, type) && pipeAlbumMessage.val.isNotEmpty) {
                var media = ChatMedia2(pipeAlbumMessage.val, MediaType2.audio);
                medias.add(media);
              }

              //IMAGE
              if (isImg(name, type) && pipeAlbumMessage.val.isNotEmpty) {
                var imageUrl = pipeAlbumMessage.val;
                if (_isBracketEnclosed(imageUrl)) {
                  imageUrl = _extractContentBetweenBrackets(imageUrl);
                }
                var media = ChatMedia2(
                  imageUrl,
                  MediaType2.image,
                );
                media.customProperties = {"data": pipeAlbumMessage};
                medias.add(media);
              }
            }
            if (buttonList.isNotEmpty) {
              chatMessage.buttonList = buttonList;
            }
            if (medias.isNotEmpty) {
              chatMessage.medias = medias;
            }
            if (chatMessage.text.isNotEmpty || chatMessage.buttonList.isNotEmpty || chatMessage.medias.isNotEmpty) {
              allMessages.add(chatMessage);
            }
          }
        }
      }

      List<QuickReply2> inputButtonList = [];
      if (messageModelList.isNotEmpty) {
        var message = messageModelList.last;
        var pipeAlbumMessage = message.content?.where((x) => x.name != "level").lastOrNull;
        if (pipeAlbumMessage != null) {
          var group = pipeAlbumMessage.group ?? "";
          var sheet = _isSheetMessage(group, isGreeting: false);
          if (sheet) {
            for (final model in message.content!) {
              var name = model.name ?? "";
              //BTN
              if (isBtn(name) && model.val.isNotEmpty) {
                if (_isSheetMessage(group, isGreeting: false)) {
                  var content = model.val;
                  var quickReply = QuickReply2(title: content);
                  quickReply.customProperties = {"data": model};
                  inputButtonList.add(quickReply);
                }
              }
            }
          }
        }
      }

      if (allMessages.isEmpty && quickInputSheetButtonList.isNotEmpty) {
        disabledInput.value = true;
      } else {
        allMessages = allMessages.reversed.toList();
        quickInputSheetButtonList.clear();
        if (inputButtonList.isNotEmpty) {
          quickInputSheetButtonList.addAll(inputButtonList);
          disabledInput.value = true;
        } else {
          disabledInput.value = false;
        }
      }
      messages.insertAll(0, allMessages);
    } catch (e) {
      logger.e(e);
    }
  }

  void clearChat() {
    HDialog.showCustom(
        title: "提示",
        content: "重启对话将重置你与ta的对话历史，确定吗？",
        confirmTitle: "确定",
        cancelTitle: "取消",
        confirmAction: () {
          ApiService().resetChat(_pipeId, () {
            messages.clear();
            quickInputSheetButtonList.clear();
            shortbtns.clear();

            _getPipeDetail();
          }, () {});
        },
        cancelAction: () {});
  }

  bool _isSheetMessage(String group, {bool isGreeting = false}) {
    Map<String, dynamic> groupMap = <String, dynamic>{};
    if (isGreeting) {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupGreeting ?? <String, dynamic>{};
    } else {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupOut ?? <String, dynamic>{};
    }
    if (groupMap.keys.contains(group)) {
      var typeMap = groupMap[group] as Map<String, dynamic>;
      if (typeMap.keys.contains("ui")) {
        var uiMap = typeMap["ui"] as Map<String, dynamic>;
        return uiMap["btnName"] == "sheet";
      }
    }
    return false;
  }

  bool _getSheetNoInputState(String group, {bool isGreeting = false}) {
    Map<String, dynamic> groupMap = <String, dynamic>{};
    if (isGreeting) {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupGreeting ?? <String, dynamic>{};
    } else {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupOut ?? <String, dynamic>{};
    }
    if (groupMap.keys.contains(group)) {
      var typeMap = groupMap[group] as Map<String, dynamic>;
      if (typeMap.keys.contains("ui")) {
        var uiMap = typeMap["ui"] as Map<String, dynamic>;
        if (uiMap.keys.contains("noInput")) {
          return uiMap["noInput"] as bool;
        }
      }
    }
    return false;
  }

  bool _isBracketEnclosed(String s) {
    RegExp regExp = RegExp(r'^\[(.*?)\]$');
    return regExp.hasMatch(s);
  }

  String _extractContentBetweenBrackets(String s) {
    RegExp regExp = RegExp(r'\[(.*?)\]');
    Match match = regExp.firstMatch(s) as Match;
    return match.group(1) ?? "";
  }

  ChatUser2 _getChatUser(String group, {bool isGreet = false}) {
    ChatUser2 chatUser = ChatUser2(id: "", nickname: '', avatarUrl: '');
    Map<String, dynamic> groupMap = <String, dynamic>{};
    if (isGreet) {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupGreeting ?? <String, dynamic>{};
    } else {
      groupMap = pipeSceneModel?.pipe.wrapper?.groupOut ?? <String, dynamic>{};
    }
    if (groupMap.keys.contains(group)) {
      var roleMap = groupMap[group] as Map<String, dynamic>;
      if (roleMap.keys.contains("role")) {
        var roleId = roleMap["role"];
        if (_memberMap.keys.contains(roleId)) {
          chatUser = _memberMap[roleId] ?? ChatUser2(id: "", nickname: '', avatarUrl: '');
        }
      }
    }
    return chatUser;
  }

  pickImage() async {
    final List<AssetEntity>? result = await AssetPicker.pickAssets(
      Get.context!,
      pickerConfig: AssetPickerConfig(
        maxAssets: 1,
        requestType: RequestType.image,
        specialPickerType: SpecialPickerType.noPreview,
        textDelegate: const AssetPickerTextDelegate(),
      ),
    );
    if (result != null) {
      final selectedAsset = result.firstOrNull;
      final image = await selectedAsset?.file;
      if (image != null) {
        final thumbImage = await _getThumbImage(image);
        selectedImagePath.value = thumbImage.path;
        _uploadImage();
      }
    }
  }

  Future<File> _getThumbImage(File imageFile) async {
    final directory = Directory.systemTemp;
    final filePath = '${directory.path}/thumbnail.jpg';
    await (it.Command()
          ..decodeImageFile(imageFile.path)
          ..copyResize(width: 600, maintainAspect: true)
          ..writeToFile(filePath))
        .executeThread();
    return File(filePath);
  }

  _uploadImage() async {
    isUploadImage.value = true;
    final file = File(selectedImagePath.value);
    ApiService().uploadImage(file, (result) {
      isUploadImage.value = false;
      String imageUrl = result["list"].first;
      if (imageUrl.isNotEmpty) {
        selectedImageUrl.value = imageUrl;
      }

      if (inputType.value == "img") {
        showSendButton.value = true;
      } else {
        if (textController.text.isNotEmpty) {
          showSendButton.value = true;
        }
      }
    }, () {
      isUploadImage.value = false;
      Fluttertoast.showToast(msg: "图片上传失败～");
    });
  }

  Future<void> requestMicrophonePermission() async {
    var status = await Permission.microphone
        .onDeniedCallback(() {
          Fluttertoast.showToast(msg: "请打开麦克风权限", gravity: ToastGravity.CENTER);
        })
        .onGrantedCallback(() {
          inputType.value = "audio";
        })
        .onPermanentlyDeniedCallback(() {
          Fluttertoast.showToast(msg: "请在设置中打开麦克风权限", gravity: ToastGravity.CENTER);
        })
        .onRestrictedCallback(() {})
        .onLimitedCallback(() {})
        .onProvisionalCallback(() {})
        .request();

    if (status.isGranted) {
      inputType.value = "audio";
    }
  }
}
